﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Noris.Tools.TraceVisualiser.Support;

namespace Noris.Tools.TraceVisualiser.Parser
{
	#region class StringParser : parser textu na segmenty (text, závorky, stringy, komentáře, atd) ale nikoliv významový lexer
	/// <summary>
	/// StringParser : parser textu na segmenty (text, závorky, stringy, komentáře, atd).
	/// Nejde o významový lexer (neprovádí významovou analýzu nalezených textů).
	/// </summary>
    public class Parser : IDisposable
    {
        #region Konstrukce a proměnné
        /// <summary>
        /// Vytvoří pracovní instanci a vloží do ní settings + provede jeho analýzu.
        /// Pokud je settings nevyhovující, dojde k chybě.
        /// </summary>
        /// <param name="setting"></param>
        private Parser(ParserSetting setting)
        {
            this._Setting = setting;
            this._Segments = new Dictionary<string, SegmentSettingAnalysed>();
            this._CurrentSettingStack = new Stack<SegmentSettingAnalysed>();
            this._AnalyseSetting();
        }
        private ParserSetting _Setting;
        private Dictionary<string, SegmentSettingAnalysed> _Segments;
        /// <summary>
        /// Aktuálně zpracovávaný segment.
        /// Je to ten, který se nachází na vrcholu zásobníku _CurrentSettingStack.
        /// Pokud by byl zásobník prázdný, vrátí referenci na this._InitialSegmentSetting.
        /// </summary>
        private SegmentSettingAnalysed _CurrentSetting
        {
            get
            {
                if (this._CurrentSettingStack.Count == 0)
                    return this._InitialSegmentSetting;
                return this._CurrentSettingStack.Peek();
            }
        }
        /// <summary>
        /// Celá hloubka aktuálně zpracovávaných segmentů
        /// </summary>
        private Stack<SegmentSettingAnalysed> _CurrentSettingStack;
        /// <summary>
        /// Výchozí segment, jeho deklarace
        /// </summary>
        private SegmentSettingAnalysed _InitialSegmentSetting;
        void IDisposable.Dispose()
        {
            this._Segments = null;
            this._Setting = null;
        }
        #endregion
        #region Analýza Settings (volá se z privátního konstruktoru třídy StringParser)
        /// <summary>
        /// Provede analýzu aktuálního Settings.
		/// Volá se z privátního konstruktoru třídy StringParser.
        /// </summary>
        private void _AnalyseSetting()
        {
            this._Segments.Clear();
            this._CurrentSettingStack.Clear();

            // Do dictionary nasypu vstupující segmentSettingy:
            foreach (ParserSegmentSetting segmentSetting in this._Setting.Segments)
                this._Segments.Add(segmentSetting.SegmentName, new SegmentSettingAnalysed(segmentSetting));

            // Nyní do každého analyzovaného settingu vložím jeho inner segmenty:
            foreach (SegmentSettingAnalysed settingAnalysed in this._Segments.Values)
            {
                if (settingAnalysed.ContainInnerSegments)
                {
					for (int i = 0; i < settingAnalysed.SegmentSetting.InnerSegmentsNames.Length; i++)
						settingAnalysed.InnerSegments[i] = this._Segments[settingAnalysed.SegmentSetting.InnerSegmentsNames[i]];
                }
            }

            // Nastavím si výchozí i aktuální segment = podle jména uvedeného v InitialSegmentName:
            this._InitialSegmentSetting = this._Segments[this._Setting.InitialSegmentName];
            this._CurrentSettingStack.Push(this._InitialSegmentSetting);
        }
		#endregion
		#region Řízení parsování textu
        /// <summary>
        /// Metoda provede parsování textu na jednotlivé segmenty a values.
        /// </summary>
        /// <param name="code"></param>
        /// <param name="setting"></param>
        /// <returns></returns>
        public static List<ParserSegment> ParseString(string code, ParserSetting setting)
        {
            using (Parser parser = new Parser(setting))
            {
                List<ParserSegment> result = parser.ParseString(code);
                return result;
            }
        }
        /// <summary>
        /// Výkonný kód parsování, instanční prostředí s analyzovaným settingem.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        protected List<ParserSegment> ParseString(string code)
        {
            string input = code;
            List<ParserSegment> result = new List<ParserSegment>();

            ParserSegment segment = new ParserSegment(null, this._InitialSegmentSetting.SegmentSetting);
			result.Add(segment);

            char[] codeChar = code.ToCharArray();
            int pointer = 0;
            int length = codeChar.Length;
            while (true)
            {
                SegmentSettingAnalysed currentSetting = this._CurrentSetting;

				int lastPointer = pointer;
                SegmentSettingAnalysed nextSetting;
                string text;
                SegmentCurrentType change = currentSetting.DetectChange(codeChar, ref pointer, out text, out nextSetting);
				
				// Nějaká forma konce:
				if (change == SegmentCurrentType.None)
					break;
				if (pointer == lastPointer)
					throw new InvalidProgramException("Metoda SegmentSettingAnalysed.DetectChange() nijak nepokročila.");

				// Nějaká forma pokroku:
				switch (change)
				{
					case SegmentCurrentType.BeginNewSegment:
                        ParserSegment nextSegment = segment.AddInnerSegment(v => new ParserSegment(v, nextSetting.SegmentSetting));
						nextSegment.Begin = text;
						nextSegment.BeginAt = lastPointer;
						segment = nextSegment;
						this._CurrentSettingStack.Push(nextSetting);
						break;

					case SegmentCurrentType.Text:
		                segment.AddText(text);
						break;

					case SegmentCurrentType.Blank:
						segment.AddBlank(text);
						break;

					case SegmentCurrentType.Delimiter:
						segment.AddDelimiter(text);
						break;

					case SegmentCurrentType.EndCurrentSegment:
                        if (currentSetting.EndOutside)
                        {   // Pokud v tomto segmentu platí, že jeho ukončující znak[y] NEJSOU součástí segmentu, ale jen deklarují jeho konec,
                            //  pak načtený text zrušíme a pointer vrátíme na původní pozici, 
                            //  zpracuje se jako součást nadřízeného segmentu v dalším cyklu s jiným settingem:
                            text = "";
                            pointer = lastPointer;
                        }
                        segment.End = text;
						segment.EndBefore = pointer;
						segment = segment.ParentSegment;
						if (segment == null)
						{
							segment = new ParserSegment(null, this._InitialSegmentSetting.SegmentSetting);
							result.Add(segment);
						}
						if (this._CurrentSettingStack.Count > 0)
							this._CurrentSettingStack.Pop();
						break;

				}
            }

            return result;
        }
		#endregion
		#region sub class SegmentSettingAnalysed : deklarace jednoho segmentu, rozšířená a obohacená o funkce pro analýzy
		/// <summary>
		/// SegmentSettingAnalysed : Třída obsahující analyzovaný vstupní setting,
        /// rozšiřující funkce a seznam povolených vnitřních segmentů (obsahuje přímo referenci na jejich analyzovaný setting)
        /// </summary>
        protected class SegmentSettingAnalysed
        {
			#region Konstrukce
            internal SegmentSettingAnalysed(ParserSegmentSetting segmentSetting)
            {
                this.SegmentSetting = segmentSetting;
				int length;

				length = segmentSetting.InnerSegmentsNames == null ? 0 : segmentSetting.InnerSegmentsNames.Length;
				this.InnerSegments = new SegmentSettingAnalysed[length];      // jen připravím prostor, ale obsah do tohoto pole se bude vkládat až v druhé fázi analýzy celého settingu.
				
				this.BeginWithChars = (segmentSetting.BeginWith == null ? new char[0] : segmentSetting.BeginWith.ToCharArray());
				this.SpecialTextsChar = this.AnalyseSpecialTexts(segmentSetting.SpecialTexts);
				this.BlanksChar = this.AnalyseStringsToChars2(segmentSetting.Blanks);
				this.DelimitersChar = this.AnalyseStringsToChars2(segmentSetting.Delimiters);
				this.EndWithChar = this.AnalyseStringsToChars2(segmentSetting.EndWith);
            }
			/// <summary>
			/// Převede pole stringů na pole polí znaků (tzv. zubaté pole).
			/// Na vstupu může být null nebo pole stringů.
			/// Vytvoří se výstupní pole, jehož počet prvků odpovídá počtu prvků vstupujícího pole stringů.
			/// Do každého prvku výstupního pole se vloží další pole, které obsahuje korespondující vstupní string rozložený do pole char.
			/// </summary>
			/// <param name="strings"></param>
			/// <returns></returns>
			private char[][] AnalyseStringsToChars2(string[] strings)
			{
				int length = (strings == null ? 0 : strings.Length);
				char[][] result = new char[length][];
				for (int i = 0; i < length; i++)
				{
					string input = strings[i];
					if (input != null)
						result[i] = input.ToCharArray();
					else
						result[i] = new char[0];
				}
				return result;
			}
			/// <summary>
			/// Převede pole ParserSegmentSpecialTexts na pole polí znaků (tzv. zubaté pole), 
			/// převádí texty z property ParserSegmentSpecialTexts.InputText
			/// Na vstupu může být null nebo pole.
			/// Vytvoří se výstupní pole, jehož počet prvků odpovídá počtu prvků vstupujícího pole stringů.
			/// Do každého prvku výstupního pole se vloží další pole, které obsahuje hodnotu korespondující 
			/// ParserSegmentSpecialTexts.InputText rozložené do pole char.
			/// </summary>
			/// <param name="specialTexts"></param>
			/// <returns></returns>
			private char[][] AnalyseSpecialTexts(ParserSegmentSpecialTexts[] specialTexts)
			{
				int length = (specialTexts == null ? 0 : specialTexts.Length);
				char[][] result = new char[length][];
				for (int i = 0; i < length; i++)
				{
					ParserSegmentSpecialTexts input = specialTexts[i];
					if (input != null && input.InputText != null)
						result[i] = input.InputText.ToCharArray();
					else
						result[i] = new char[0];
				}
				return result;
			}
			public override string ToString()
			{
				return this.SegmentSetting.ToString();
			}
			#endregion
			#region Property
			/// <summary>
			/// Vstupní data setting = od zadavatele
			/// </summary>
			internal ParserSegmentSetting SegmentSetting { get; private set; }
			/// <summary>
			/// Pole vnořených definic segmentů (již analyzovné definice)
			/// </summary>
			internal SegmentSettingAnalysed[] InnerSegments { get; private set; }
			/// <summary>
			/// Deklarace začátku tohoto segmentu
			/// </summary>
			internal char[] BeginWithChars { get; private set; }
			/// <summary>
			/// Příznak, zda tento segment má deklarovaný počátečení text (this.BeginWithChars)
			/// </summary>
			internal bool HasBegin { get { return (this.BeginWithChars.Length > 0); } }
			/// <summary>
			/// Deklarace možných zakončení tohoto segmentu
			/// </summary>
			internal char[][] EndWithChar { get; private set; }
            /// <summary>
            /// true: Oddělovače na konci tohoto segmentu (this.EndWith) se nevkládají do konce segmentu (Segment.End), 
            /// ale zpracují se jako znaky následující za segmentem.
            /// Typicky jde o konec řádkového komentáře (Cr, Lf), které nejsou součástí komentáře, ale jde o standardní znak konce řádku v parent segmentu.
            /// </summary>
            internal bool EndOutside { get { return this.SegmentSetting.EndOutside; } }
			/// <summary>
			/// Deklarace možných textů Blank v tomto segmentu
			/// </summary>
			internal char[][] BlanksChar { get; private set; }
			/// <summary>
			/// Deklarace možných textů Delimiter v tomto segmentu
			/// </summary>
			internal char[][] DelimitersChar { get; private set; }
			/// <summary>
			/// Deklarace vstupních hodnot speciálních textů v tomto segmentu (this.SpecialTexts[].InputText rozdělené na zubaté pole znaků)
			/// </summary>
			internal char[][] SpecialTextsChar { get; private set; }
			/// <summary>
			/// Deklarace speciálních textů, pole deklarací párů (přímý přístup do this.SegmentSetting.SpecialTexts)
			/// </summary>
			internal ParserSegmentSpecialTexts[] SpecialTexts { get { return this.SegmentSetting.SpecialTexts; } }
			/// <summary>
            /// Název segmentu, klíčové slovo
            /// </summary>
            public string SegmentName { get { return this.SegmentSetting.SegmentName; } }
            /// <summary>
            /// Seznam segmentů, které se mohou vyskytovat uvnitř tohoto elementu.
            /// Typicky v závorce může být string nebo další závorky.
            /// </summary>
            public string[] InnerSegmentsNames { get { return this.SegmentSetting.InnerSegmentsNames; } }
            /// <summary>
            /// true, pokud tento segment může obsahovat speciální bloky textu.
            /// </summary>
            public bool ContainSpecialTexts { get { return (this.SpecialTexts != null && this.SpecialTexts.Length > 0); } }
            /// <summary>
            /// true, pokud tento segment může obsahovat nějaké vnořené vnitřní segmenty. Typicky v závorce může být string nebo další závorky.
            /// </summary>
            public bool ContainInnerSegments { get { return (this.InnerSegmentsNames != null && this.InnerSegmentsNames.Length > 0); } }
			#endregion
			#region Detekce vstupního textu
			/// <summary>
            /// Určí, co to v textu je. Zda náš segment pokračuje, nebo končí, nebo začíná jiný segment.
            /// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text"></param>
            /// <param name="nextSetting"></param>
            /// <returns></returns>
            internal SegmentCurrentType DetectChange(char[] input, ref int pointer, out string text, out SegmentSettingAnalysed nextSetting)
            {
                nextSetting = null;
				text = null;

				// Pokud je vstup prázdný:
				if (input == null || pointer >= input.Length)
					return SegmentCurrentType.None;

				// Zjistíme, zda vstupující text je/není nějaký náš speciální znak:
                if (this.IsSpecialText(input, ref pointer, out text))
					return SegmentCurrentType.Text;

				// Zjistíme, zda vstupující text je/není koncem aktuálního segmentu:
				if (this.IsEndOfThisSegment(input, ref pointer, out text))
					return SegmentCurrentType.EndCurrentSegment;

				// Zjistíme, zda vstupující text je/není začátkem některého z našich vnořených segmentů:
				if (this.IsBeginOfInnerSegment(input, ref pointer, out text, out nextSetting))
					return SegmentCurrentType.BeginNewSegment;

				// Detekujeme Blank:
				if (this.IsBlank(input, ref pointer, out text))
					return SegmentCurrentType.Blank;

				// Detekujeme Delimiter:
				if (this.IsDelimiter(input, ref pointer, out text))
					return SegmentCurrentType.Delimiter;

				// Aktuální text není nic zvláštního, je to běžný znak:
				text = input[pointer].ToString();
				pointer++;
				return SegmentCurrentType.Text;
            }
			/// <summary>
			/// Vrátí true, pokud aktuální text odpovídá některému speciálnímu textu.
			/// Pokud ano, pak vrací true, nastavuje out text a posouvá pointer o délku vstupního textu, 
			/// takže ukazuje na první následující znak.
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text">Výstup textu, který se má zapsat Segment.Value.Content</param>
			/// <returns></returns>
			protected bool IsSpecialText(char[] input, ref int pointer, out string text)
            {
				int index;
				if (IsMatching(input, ref pointer, this.SpecialTextsChar, out text, out index))
				{	// Našli jsme odpovídající vstupní text, ale vracet budeme text výstupní (převod Input => Output)
					text = this.SpecialTexts[index].OutputText;
					return true;
				}
				return false;
            }
			/// <summary>
			/// Vrátí true, pokud aktuální text odpovídá konci aktuálního segmentu.
			/// Pokud ano, pak vrací true, nastavuje out text a posouvá pointer o délku vstupního textu, 
			/// takže ukazuje na první následující znak.
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text">Výstup textu, který se má zapsat do Segment.End</param>
			/// <returns></returns>
			protected bool IsEndOfThisSegment(char[] input, ref int pointer, out string text)
			{
				return IsMatching(input, ref pointer, this.EndWithChar, out text);
			}
			/// <summary>
			/// Vrátí true, pokud aktuální text odpovídá začátku některého z našich vnitřních segmentů.
			/// Pokud ano, pak vrací true, nastavuje out text a posouvá pointer o délku vstupního textu, 
			/// takže ukazuje na první následující znak.
			/// Ukládá referenci na následující segment do out parametru.
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text">Výstup textu, který se má zapsat do nového segmentu do Segment.Begin</param>
			/// <param name="nextSetting">Reference na objekt Setting, který tímto začíná (pokud je vráceno true)</param>
			/// <returns></returns>
			protected bool IsBeginOfInnerSegment(char[] input, ref int pointer, out string text, out SegmentSettingAnalysed nextSetting)
			{
				if (this.ContainInnerSegments)
				{
					// Pozor: segmentů může být více, a mohou i podobně začínat.
					//    My si z takových vybereme ten segment, jehož začátek je nejdelší.
					// Příklad: máme C# komentář začínající //
					// a přitom máme XML komentář začínající ///
					// Pokud ve vstupujícím textu najdeme /// pak jde o XML komentář:
					int maxIndex = -1;
					int maxLength = 0;
					for (int i = 0; i < this.InnerSegments.Length; i++)
					{
						SegmentSettingAnalysed settingAnalysed = this.InnerSegments[i];
						if (settingAnalysed.HasBegin && StartWith(input, pointer, settingAnalysed.BeginWithChars))
						{
							int currLength = settingAnalysed.BeginWithChars.Length;
							if (currLength > maxLength)
							{
								maxIndex = i;
								maxLength = currLength;
							}
						}
					}

					// Pokud jsem našel nějaký segment, pak jej vrátíme:
					if (maxIndex >= 0)
					{
						nextSetting = this.InnerSegments[maxIndex];
						text = GetStringFromChars(nextSetting.BeginWithChars);
						pointer += nextSetting.BeginWithChars.Length;
						return true;
					}
				}
				nextSetting = null;
				text = null;
				return false;
			}
			/// <summary>
			/// Vrátí true, pokud aktuální text odpovídá některému textu Blank.
			/// Pokud ano, pak vrací true, nastavuje out text a posouvá pointer o délku vstupního textu, 
			/// takže ukazuje na první následující znak.
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text">Výstup textu, který se má zapsat do Segment.End</param>
			/// <returns></returns>
			protected bool IsBlank(char[] input, ref int pointer, out string text)
			{
				return IsMatching(input, ref pointer, this.BlanksChar, out text);
			}
			/// <summary>
			/// Vrátí true, pokud aktuální text odpovídá některému textu Delimiters.
			/// Pokud ano, pak vrací true, nastavuje out text a posouvá pointer o délku vstupního textu, 
			/// takže ukazuje na první následující znak.
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="text">Výstup textu, který se má zapsat do Segment.End</param>
			/// <returns></returns>
			protected bool IsDelimiter(char[] input, ref int pointer, out string text)
			{
				return IsMatching(input, ref pointer, this.DelimitersChar, out text);
			}
			/// <summary>
			/// Zjistí, zda text ve vstupním bufferu odpovídá některému ze vzorků uvedených v poli patterns.
			/// Pokud ano, vrátí true a nastaví odpovídající text do out parametru text, a posune pointer o odpovídající délku.
			/// Pokud ne, vrátí false a pointer neposouvá.
			/// Ze zadaných vzorků se pokouší najít ten nejdelší odpovídající.
			/// </summary>
			/// <param name="input">Vstupní buffer</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="patterns">Sada hledaných vzorků, uložených jako zubaté pole</param>
			/// <param name="text">Výstup nalezeního textu</param>
			/// <param name="index">Index, na kterém je nalezený vzorek</param>
			/// <returns></returns>
			internal static bool IsMatching(char[] input, ref int pointer, char[][] patterns, out string text)
			{
				int index;
				return IsMatching(input, ref pointer, patterns, out text, out index);
			}
			/// <summary>
			/// Zjistí, zda text ve vstupním bufferu odpovídá některému ze vzorků uvedených v poli patterns.
			/// Pokud ano, vrátí true a nastaví odpovídající text do out parametru text, a posune pointer o odpovídající délku.
			/// Pokud ne, vrátí false a pointer neposouvá.
			/// Ze zadaných vzorků se pokouší najít ten nejdelší odpovídající.
			/// </summary>
			/// <param name="input">Vstupní buffer</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="patterns">Sada hledaných vzorků, uložených jako zubaté pole</param>
			/// <param name="text">Výstup nalezeního textu</param>
			/// <param name="index">Index, na kterém je nalezený vzorek</param>
			/// <returns></returns>
			internal static bool IsMatching(char[] input, ref int pointer, char[][] patterns, out string text, out int index)
			{
				if (patterns != null)
				{
					// Hledám nejdelší shodný vzorek:
					int matchIndex = -1;
					int maxLength = 0;
					int length = patterns.Length;
					for (int i = 0; i < length; i++)
					{
						char[] pattern = patterns[i];
						if (StartWith(input, pointer, pattern))
						{
							if (pattern.Length > maxLength)
							{
								matchIndex = i;
								maxLength = pattern.Length;
							}
						}
					}
					// Pokud jsme našli, nastavím out hodnoty a skončím s true:
					if (matchIndex >= 0)
					{
						index = matchIndex;
						char[] pattern = patterns[index];
						pointer += pattern.Length;
						text = GetStringFromChars(pattern);
						return true;
					}
				}
				// Nenašli anebo nebylo možno hledat:
				text = null;
				index = -1;
				return false;
			}
			/// <summary>
			/// Vrátí true, pokud vstupní buffer počínaje od dané pozice obsahuje text daný v patternu
			/// </summary>
			/// <param name="input">Vstupní text uložený v bufferu</param>
			/// <param name="pointer">Ukazatel do bufferu</param>
			/// <param name="pattern">Hledaný vzorek</param>
			/// <returns></returns>
			internal static bool StartWith(char[] input, int pointer, char[] pattern)
			{
				if (pattern == null) return false;
				int patternLength = pattern.Length;              // Délka hledaného textu
				int inputLength = input.Length - pointer;        // Délka vstupního textu
				if (patternLength > inputLength) return false;   // Na vstupu je menší text než hledáme.
				for (int i = 0; i < patternLength; i++)
				{
					if (input[pointer + i] != pattern[i]) return false;
				}
				return true;
			}
			/// <summary>
			/// Vrátí text vytvořený z daného pole znaků
			/// </summary>
			/// <param name="pattern"></param>
			/// <returns></returns>
			internal static string GetStringFromChars(char[] pattern)
			{
				return String.Concat(pattern);
			}
			#endregion
		}
		#endregion
		#region enum SegmentCurrentType : druh textu v aktuální pozici bufferu, řídí zpracování textu do segmentů
		/// <summary>
		/// SegmentCurrentType : druh textu v aktuální pozici bufferu, řídí zpracování textu do segmentů.
		/// </summary>
		protected enum SegmentCurrentType
        {
			/// <summary>
			/// Není text, konec.
			/// </summary>
            None = 0,
			/// <summary>
			/// Mezera mezi texty.
			/// Stále jsme uvnitř aktuálního segmentu.
			/// </summary>
            Blank,
			/// <summary>
			/// Text.
			/// Stále jsme uvnitř aktuálního segmentu.
			/// </summary>
			Text,
			/// <summary>
			/// Oddělovat textů.
			/// Stále jsme uvnitř aktuálního segmentu.
			/// </summary>
			Delimiter,
			/// <summary>
			/// Na aktuální pozici začíná nový vnořený segment.
			/// </summary>
            BeginNewSegment,
			/// <summary>
			/// Na aktuální pozici končí aktuální segment. 
			/// Vracíme se k segmentu, který je naším parentem.
			/// </summary>
            EndCurrentSegment
		}
		#endregion
	}
	#endregion
    #region class ParserSegment : Segment obsahující data (text a/nebo řadu dalších vnořených segmentů)
    /// <summary>
    /// ParserSegment : Segment obsahující data (text a/nebo řadu dalších vnořených segmentů)
    /// Je uvedeno jeho jméno a parent.
    /// Vše, co obsahuje, je uloženo v řadě hodnot Values.
    /// </summary>
    public class ParserSegment
    {
        #region Konstrukce a property
        /// <summary>
        /// Vytvoří nový segment
        /// </summary>
        /// <param name="parentValue">Parent value, níž je tento segment hostován jako InnerSegment. Může být null.</param>
        /// <param name="segmentSetting">Setting segmentu, podle kterého byl tento segment detekován a vytvořen. Mimo jiné nese informace o RTF formátování.</param>
        public ParserSegment(ParserSegmentValue parentValue, ParserSegmentSetting segmentSetting)
        {
            this.ParentValue = parentValue;
			this.SegmentSetting = segmentSetting;
            this._Values = new List<ParserSegmentValue>();
        }
        /// <summary>
        /// Vytvoří nový segment
        /// </summary>
        /// <param name="parentValue">Parent value, níž je tento segment hostován jako InnerSegment. Může být null.</param>
        /// <param name="parallelSegment">Segment, který má shodný charakter jako segment právě vytvářený. 
        /// Nový segment si z něj převezme nastavení (Setting) a hodnoty Begin a End. 
        /// Nepřebírá ParentValue (tu dostává explicitně), nepřebírá pozice BeginAt a EndBefore, ani obsah Values.
        /// </param>
        public ParserSegment(ParserSegmentValue parentValue, ParserSegment parallelSegment)
        {
            this.ParentValue = parentValue;
            this.SegmentSetting = parallelSegment.SegmentSetting;
            this.Begin = parallelSegment.Begin;
            this.End = parallelSegment.End;
            this._Values = new List<ParserSegmentValue>();
        }
        /// <summary>
        /// Parent segment, v jehož některé Values je tento segment umístěn (konkrétní Value, v níž je tento segment hostován, je uložena v this.ParentValue).
        /// Typicky při vnořování závorek: vnitřní závorka zde má odkaz na vnější závorku.
        /// String může mít odkaz na závorku v níž se nachází nebo na vnější kód (code).
        /// </summary>
        public ParserSegment ParentSegment { get { return (this.ParentValue == null ? null : this.ParentValue.Parent); } }
        /// <summary>
        /// Parent value, níž je tento segment hostován jako InnerSegment.
        /// </summary>
        public ParserSegmentValue ParentValue { get; private set; }
		/// <summary>
		/// Nastavení tohoto segmentu. Mimo jiné nese informace o RTF formátování.
		/// </summary>
		protected ParserSegmentSetting SegmentSetting { get; private set; }
        /// <summary>
        /// Název segmentu - vychází z jeho definice
        /// </summary>
		public string SegmentName { get { return this.SegmentSetting.SegmentName; } }
        /// <summary>
        /// Text, kterým segment začíná
        /// </summary>
        public string Begin { get; set; }
        /// <summary>
        /// Příznak, že tento segment má neprázdný this.Begin
        /// </summary>
        public bool HasBegin { get { return (this.Begin != null && this.Begin.Length > 0); } }
		/// <summary>
		/// Pozice ve vstupním textu (bufferu), na které segment začíná
		/// </summary>
		public int BeginAt { get; set; }
		/// <summary>
        /// Obsah segmentu (texty, segmenty)
        /// </summary>
        public IEnumerable<ParserSegmentValue> Values { get { return this._Values; } }
        private List<ParserSegmentValue> _Values;
        /// <summary>
        /// Text, kterým segment končí
        /// </summary>
        public string End { get; set; }
        /// <summary>
        /// Příznak, že tento segment má neprázdný this.End
        /// </summary>
        public bool HasEnd { get { return (this.End != null && this.End.Length > 0); } }
        /// <summary>
		/// Pozice ve vstupním textu (bufferu), před kterou segment končí.
		/// Rozdíl (EndBefore - BeginAt) = počet znaků tohoto segmentu včetně Inner.
		/// Na pozici EndBefore je první znak dalšího segmentu.
		/// </summary>
		public int EndBefore { get; set; }
        /// <summary>
        /// Plný text segmentu, včetně úvodního a koncového textu (uvozovky, závorky) a včetně vnořených hodnot (rozvinutých do textu).
		/// I vnitřní vnořené hodnoty mají uvedeny i svoje úvodní a koncové texty.
		/// Příklad: text v závorce: (abc * def * (x + y)) je tedy vrácen shodně: (abc * def * (x + y))
		/// Text v oddělovačích «{proměnná}» je vrácen včetně oddělovačů: «{proměnná}»
		/// Na rozdíl od property InnerText, která vrací text bez vnějších oddělovačů.
        /// </summary>
        public string Text
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                if (this.Begin != null) sb.Append(this.Begin);
                foreach (ParserSegmentValue value in this.Values)
                    sb.Append(value.ToString());
                if (this.End != null) sb.Append(this.End);
                return sb.ToString();
            }
        }
		/// <summary>
		/// Text segmentu, bez úvodního a koncového textu (uvozovky, závorky), ale obsahuje vnořené vnitřní hodnoty.
		/// Pozor: vnitřní vnořené hodnoty ale mají uvedeny i svoje úvodní a koncové texty.
		/// Příklad: text v závorce: (abc * def * (x + y)) je tedy vrácen bez vnějších závorek, ale s vnitřními závorkami: abc * def * (x + y)
		/// Text v oddělovačích «{proměnná}» je vrácen bez oddělovačů: proměnná
		/// Na rozdíl od property Text, která vrací text včetně vnějších oddělovačů.
		/// </summary>
		public string InnerText
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				foreach (ParserSegmentValue value in this.Values)
					sb.Append(value.ToString());
				return sb.ToString();
			}
		}
		/// <summary>
		/// Text segmentu, bez úvodního a koncového textu (uvozovky, závorky), 
		/// a navíc namísto vnitřních segmentů jsou tři tečky (ohraničené originálním oddělovačem).
		/// </summary>
		public string SimpleText
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				foreach (ParserSegmentValue value in this.Values)
					sb.Append(value.SimpleText);
				return sb.ToString();
			}
		}
		/// <summary>
		/// Zjednodušený text: obsahuje úvodní oddělovač (Begin), tři tečky a koncový oddělovač.
		/// </summary>
		public string SimplifiedText
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				if (this.Begin != null) sb.Append(this.Begin);
				sb.Append("...");
				if (this.End != null) sb.Append(this.End);
				return sb.ToString();
			}
		}
		/// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        #endregion
        #region Přidávání dat
		/// <summary>
		/// Zajistí přidání textu do tohoto segmentu.
		/// Pokud na poslední pozici v řadě this.Values je objekt typu Text, předá daný text do něj na konec.
		/// Pokud je na poslední pozici nějaký jiný typ, založí nový objekt do this.Values s typem Text, a text vloží do něj.
		/// </summary>
		/// <param name="text"></param>
		public void AddText(string text)
        {
            if (this._Values.Count == 0 || this._Values[this._Values.Count - 1].ValueType != ParserSegmentValueType.Text)
                this._Values.Add(new ParserSegmentValue(this, ParserSegmentValueType.Text));
            this._Values[this._Values.Count - 1].AddContent(text);
        }
		/// <summary>
		/// Zajistí přidání prázdného textu do tohoto segmentu.
		/// Pokud na poslední pozici v řadě this.Values je objekt typu Blank, předá daný text do něj na konec.
		/// Pokud je na poslední pozici nějaký jiný typ, založí nový objekt do this.Values s typem Blank, a text vloží do něj.
		/// </summary>
		/// <param name="text"></param>
		public void AddBlank(string text)
		{
            if (this._Values.Count == 0 || this._Values[this._Values.Count - 1].ValueType != ParserSegmentValueType.Blank)
                this._Values.Add(new ParserSegmentValue(this, ParserSegmentValueType.Blank));
            this._Values[this._Values.Count - 1].AddContent(text);
		}
		/// <summary>
		/// Do tohoto segmentu přidá nový objekt typu Delimiter a předaný text do něj uloží.
		/// Obsah delimiteru se ukládá vždy samostatně, nikdy se nesloučí dva delimitery do jednoho objektu.
		/// </summary>
		/// <param name="text"></param>
		public void AddDelimiter(string text)
		{
			this._Values.Add(new ParserSegmentValue(this, ParserSegmentValueType.Delimiter, text));
		}
		/// <summary>
        /// Do tohoto segmentu přidá dodaný objekt Value.
		/// </summary>
		/// <param name="value"></param>
		public void AddValue(ParserSegmentValue value)
		{
			value.Parent = this;             // Změním parenta. Value může být doma jen v jednom segmentu.
			this._Values.Add(value);
		}
        /// <summary>
        /// Do tohoto segmentu přidá řadu dodaných objektů Value.
        /// </summary>
        /// <param name="values"></param>
        public void AddValues(IEnumerable<ParserSegmentValue> values)
        {
            foreach (ParserSegmentValue value in values)
            {
                value.Parent = this;         // Změním parenta. Value může být doma jen v jednom segmentu.
                this._Values.Add(value);
            }
        }
        /// <summary>
        /// Do tohoto segmentu do jeho seznamu Values přidá nový objekt typu ParserSegmentValue (ValueType = InnerSegment).
        /// Do tohoto objektu Value vytvoří nový segment a vloží jej do value.InnerSegment.
        /// Vytvořený InnerSegment vrátí.
        /// Pro vytvoření nového segmentu použije dodanou metodu (parametr segmentCreator).
        /// </summary>
        /// <param name="segmentCreator"></param>
        /// <returns></returns>
        internal ParserSegment AddInnerSegment(Func<ParserSegmentValue, ParserSegment> segmentCreator)
        {
            ParserSegmentValue value = new ParserSegmentValue(this, ParserSegmentValueType.InnerSegment);
            this._Values.Add(value);

            ParserSegment innerSegment = segmentCreator(value);
            value.InnerSegment = innerSegment;
            return innerSegment;
        }
		#endregion
        #region Změna charakteru segmentu
        /// <summary>
        /// Změní charakter tohoto segmentu na nově zadaný.
        /// Dohledá odpovídající setting. Uloží nově zadaný Begin a End, ale nemění pozice BeginAt a EndBefore (ty se vztahují k původnímu textu).
        /// Umožní založit novou value daného typu, jména a obsahu.
        /// </summary>
		/// <param name="segmentName"></param>
		/// <param name="begin">Text, kterým bude segment začínat (uvozovky, apostrof, závorka)</param>
		/// <param name="end">Text, kterým bude segment končit (uvozovky, apostrof, závorka)</param>
        /// <param name="valueType"></param>
        /// <param name="valueName"></param>
        /// <param name="content"></param>
        public void ChangeTo(string segmentName, string begin, string end, ParserSegmentValueType valueType, string valueName, string content)
        {
            this.Begin = (begin == null ? "" : begin);
            this.End = (end == null ? "" : end);
            ParserSegmentSetting segmentSetting = this.SegmentSetting.Parent.Segments.FirstOrDefault(s => s.SegmentName == segmentName);
            if (segmentSetting != null)
                this.SegmentSetting = segmentSetting;
            
            this._Values.Clear();
            if (valueType != ParserSegmentValueType.None)
            {
                ParserSegmentValue value = new ParserSegmentValue(this, valueType, content);
                value.ValueName = valueName;
                this._Values.Add(value);
            }
        }
        #endregion
        #region Vyhledání a filtrování parsovaných dat
        /// <summary>
		/// Najde a vrátí seznam segmentů, které vyhovují dané podmínce (predicate).
		/// Může rekurzivně prohledávat i vnořené segmenty, podle parametru recursive.
		/// </summary>
		/// <param name="predicate">Podmínka</param>
		/// <param name="recursive">Prohledávat i vnořené segmenty?</param>
		/// <returns></returns>
		public List<ParserSegment> FindAllSegments(Func<ParserSegment, bool> predicate, bool recursive)
		{
			List<ParserSegment> result = new List<ParserSegment>();
			this.AddAllSegments(result, predicate, recursive);
			return result;
		}
		/// <summary>
		/// Do daného seznamu přidá sebe pokud vyhovuje dané podmínce, a případně i rekurzivně pro svoje InnerSegments.
		/// </summary>
		/// <param name="list"></param>
		/// <param name="predicate"></param>
		/// <param name="recursive"></param>
		protected void AddAllSegments(List<ParserSegment> list, Func<ParserSegment, bool> predicate, bool recursive)
		{
			if (predicate(this))
				list.Add(this);
			if (recursive)
			{
				foreach (ParserSegmentValue value in this.Values.Where(v => v.HasInnerSegment))
					value.InnerSegment.AddAllSegments(list, predicate, recursive);
			}
		}
		/// <summary>
		/// Najde a vrátí seznam hodnot, které vyhovují dané podmínce (predicate).
		/// Může rekurzivně prohledávat i vnořené segmenty, podle parametru recursive.
		/// </summary>
		/// <param name="predicate">Podmínka</param>
		/// <param name="recursive">Prohledávat i vnořené segmenty?</param>
		/// <returns></returns>
		public List<ParserSegmentValue> FindAllValues(Func<ParserSegmentValue, bool> predicate, bool recursive)
		{
			List<ParserSegmentValue> result = new List<ParserSegmentValue>();
			this.AddAllValues(result, predicate, recursive);
			return result;
		}
		/// <summary>
		/// Do daného seznamu přidá svoje Values, které vyhovují podmínce.
		/// </summary>
		/// <param name="list"></param>
		/// <param name="predicate"></param>
		/// <param name="recursive"></param>
		protected void AddAllValues(List<ParserSegmentValue> list, Func<ParserSegmentValue, bool> predicate, bool recursive)
		{
			foreach (ParserSegmentValue value in this.Values)
			{
				if (predicate(value))
					list.Add(value);
				if (recursive && value.HasInnerSegment)
					value.InnerSegment.AddAllValues(list, predicate, recursive);
			}
		}
		#endregion
		#region Generování RTF kódu pro editaci
		/// <summary>
		/// Obsahuje kompletní RTF text tohoto segmentu (včetně hlavičky).
        /// Upozornění: RTF text je určen k zobrazení a k editaci, tedy i k následnému opakovanému parsování.
        /// Proto RTF text musí obsahovat speciální znaky psané tak, jak jsou psány ve vstupu, nikoliv tak, jak jsou v čistém textu segmentu.
        /// Příklad: v C# máme string "Aaaa\"Bbbb", což značí že uprostřed je uvozovka. Čistá hodnota (Text) je Aaaa"Bbbb .
        /// Do RTF textu se ale dostává hodnota včetně oddělovačů (uvozovky na okrajích), a vnitřní uvozovka se do RTF textu vepisuje jako na vstupu (se zpětným lomítkem).
        /// Obdobně v SQL stringu mějme string = 'Aaaa''Bbbb', čistý Text = Aaaa'Bbbb a Rtf text musí opět nést uprostřed apostrofy dva.
		/// </summary>
		public string RtfText { get { return this.GetRtfText(); } }
        /// <summary>
        /// Seznam všech RTF items tohoto segmentu.
        /// Výstup lze vložit do RTF coderu.
        /// </summary>
        public IEnumerable<RtfItem> RtfItems { get { return this.GetRtfItems(); } }
        /// <summary>
        /// Vrátí kompletní RTF text za tento segment jako string.
        /// </summary>
        /// <returns></returns>
        protected string GetRtfText()
		{
			using (RtfCoder rtfCoder = new RtfCoder())
			{
                List<RtfItem> rtfItems = this.GetRtfItems();
                rtfCoder.AddRange(rtfItems);
				return rtfCoder.RtfText;
			}
		}
        /// <summary>
        /// Vrátí položky RTF items, které je možno vkládat do RTF coderu.
        /// </summary>
        /// <returns></returns>
        protected List<RtfItem> GetRtfItems()
        {
            List<RtfItem> rtfItems = new List<RtfItem>();
            this.AddRtfValues(rtfItems);
            return rtfItems;
        }
        /// <summary>
		/// Do RTF kodéru vloží data tohoto segmentu.
		/// Data jsou: 
		/// 1. Zahájení (nastavení fontu, dané definicí segmentu)
		/// 2. Tělo (obsah this.Values):
		///  a) Content
		///   aa) zahájení contentu (nastavení fontu platné pro tento segment)
		///   ab) vlastní text contentu
		///  b) Vnořené segmenty, rekurzivně od bodu 1)
		/// 3. Ukončení segmentu
		/// </summary>
		/// <param name="rtfCoder"></param>
        protected void AddRtfValues(List<RtfItem> rtfItems)
		{
			// 1. Zahájení (nastavení fontu, dané definicí segmentu + výpis textu Begin, typicky uvozovky nebo jiná úvodní sekvence segmentu:
            if (this.HasBegin)
            {
                this.SegmentSetting.RtfCoderAddCodesSet(rtfItems, this);
                rtfItems.Add(RtfItem.NewText(this.Begin));
                this.SegmentSetting.RtfCoderAddCodesReset(rtfItems, this);
            }

			// 2. Tělo (obsah this.Values):
			foreach (ParserSegmentValue value in this.Values)
			{
				switch (value.ValueType)
				{
					case ParserSegmentValueType.Blank:
					case ParserSegmentValueType.Text:
					case ParserSegmentValueType.Delimiter:
						// a) Content
                        this.AddRtfContent(rtfItems, value);
						break;
					case ParserSegmentValueType.InnerSegment:
						// b) Vnořené segmenty, rekurzivně od bodu 1)
                        value.InnerSegment.AddRtfValues(rtfItems);
						break;
				}
			}

			// 3. Ukončení segmentu
            if (this.HasEnd)
            {
                this.SegmentSetting.RtfCoderAddCodesSet(rtfItems, this);
                rtfItems.Add(RtfItem.NewText(this.End));
                this.SegmentSetting.RtfCoderAddCodesReset(rtfItems, this);
            }
		}
        /// <summary>
        /// Do RTF kodéru vloží text odpovídající textu tohoto segmentu (který má být IsContent).
        /// Pozor: v textu se musí reverzně nahradit speciální znaky!
        /// </summary>
        /// <param name="rtfCoder"></param>
        /// <param name="value"></param>
        /// <param name="rtfIsSet"></param>
        protected void AddRtfContent(List<RtfItem> rtfItems, ParserSegmentValue value)
		{
            if (!value.HasContent) return;

			// aa) zahájení contentu (nastavení fontu platné pro tento segment a aktuální typ Value)
            this.SegmentSetting.RtfCoderAddCodesSet(rtfItems, value);

			// ab) vlastní text contentu
            string content = value.Content;
            string rtfContent = ReverseReplaceSpecialTexts(this.SegmentSetting, content);
            rtfItems.Add(RtfItem.NewText(rtfContent));

            // ac) ukončení contentu (zrušení fontu a dalších nastavení pro tento segment a aktuální typ Value)
            this.SegmentSetting.RtfCoderAddCodesReset(rtfItems, value);
        }
        /// <summary>
        /// Provede reverzní náhradu speciálních textů: z čistých (Output) do vstupních (Input)
        /// </summary>
        /// <param name="segmentSetting"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        protected static string ReverseReplaceSpecialTexts(ParserSegmentSetting segmentSetting, string content)
        {
            string result = null;
            if (content != null)
            {
                result = content;
                if (segmentSetting.SpecialTexts != null && segmentSetting.SpecialTexts.Length > 0)
                {
                    foreach (ParserSegmentSpecialTexts specialText in segmentSetting.SpecialTexts)
                    {
                        if (result.Contains(specialText.OutputText))
                            result = result.Replace(specialText.OutputText, specialText.InputText);
                    }
                }
            }
            return result;
        }
        #endregion
    }
    #endregion
    #region class ParserSegmentValue : Jednotlivá hodnota v segmentu (text nebo segment).
    /// <summary>
    /// Jednotlivá hodnota v segmentu.
    /// Může to být nijak nespecifikovaný text, nebo to může být vnitřní segment.
    /// Obsahuje odkaz na svůj Parent segment.
    /// </summary>
    public class ParserSegmentValue
    {
        #region Konstrukce
        public ParserSegmentValue(ParserSegment parent, ParserSegmentValueType valueType)
        {
            this.Parent = parent;
			this.ValueType = valueType;
			this.ContentBuilder = new StringBuilder();
        }
		public ParserSegmentValue(ParserSegment parent, ParserSegmentValueType valueType, string text)
		{
			this.Parent = parent;
			this.ValueType = valueType;
			this.ContentBuilder = new StringBuilder(text);
		}
		public ParserSegmentValue(ParserSegment parent, ParserSegmentValueType valueType, ParserSegment innerSegment)
        {
            this.Parent = parent;
			this.ValueType = valueType;
			this.InnerSegment = innerSegment;
			this.ContentBuilder = null;
        }
		protected string ContentCache;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        #endregion
        #region Property
        /// <summary>
        /// Parent segment, v němž je tato hodnota umístěna.
        /// Je nastaveno vždy.
        /// </summary>
        public ParserSegment Parent { get; internal set; }
        /// <summary>
        /// Název hodnoty v tomto segmentu.
        /// 
        /// </summary>
        public string ValueName { get; set; }
        /// <summary>
		/// Druh hodnoty v tomto segmentu
		/// </summary>
		public ParserSegmentValueType ValueType { get; private set; }
		/// <summary>
        /// Text, který je v této části obsažen (pokud je zadán = není null, pak musí být null vnořený segment this.Segment).
		/// Text je obsažen v prvcích typu Blank, Text, Delimiter.
        /// </summary>
		public string Content
		{
			get
			{
				if (!this.HasContent) return null;
				if (this.ContentCache == null)
					this.ContentCache = this.ContentBuilder.ToString();
				return this.ContentCache;
			}
		}
		/// <summary>
		/// true, pokud tento prvek má textový obsah (Content).
		/// To jest pro prvek typu Blank nebo Text nebo Delimiter, a když v Content je text dělší než 0.
		/// </summary>
		public bool HasContent
		{
			get
			{
				return ((this.ValueType == ParserSegmentValueType.Blank || this.ValueType == ParserSegmentValueType.Text || this.ValueType == ParserSegmentValueType.Delimiter)
						&& this.ContentBuilder != null && this.ContentBuilder.Length > 0);
			}
		}
		/// <summary>
		/// true, pokud tento prvek má vnitřní segment (InnerSegment).
		/// To jest pro prvek typu InnerSegment, a když v InnerSegment není null.
		/// </summary>
		public bool HasInnerSegment
		{
			get
			{
				return (this.ValueType == ParserSegmentValueType.InnerSegment && this.InnerSegment != null);
			}
		}
		/// <summary>
		/// Zde se skládá text postupným přidáváním
		/// </summary>
		protected StringBuilder ContentBuilder;
        /// <summary>
        /// Vnitřní vnořený segment, může obsahovat řadu dalších hodnot. Pokud není null, pak musí být null Content.
        /// </summary>
        public ParserSegment InnerSegment
        {
            get { return this._InnerSegment; }
            set
            {
                if (this.ValueType != ParserSegmentValueType.InnerSegment)
                    throw new InvalidOperationException("Nelze vložit objekt do property ParserSegmentValue.InnerSegment, pokud ValueType není InnerSegment.");
                if (this._InnerSegment != null)
                    throw new InvalidOperationException("Nelze vložit objekt do property ParserSegmentValue.InnerSegment, pokud tato property již obsahuje nějaký jiný objekt.");
                this._InnerSegment = value;
            }
        }
        private ParserSegment _InnerSegment;
        /// <summary>
		/// Plný text hodnoty, včetně úvodního a koncového textu (uvozovky, závorky) a včetně vnořených hodnot (rozvinutých do textu).
		/// I vnitřní vnořené hodnoty mají uvedeny i svoje úvodní a koncové texty.
		/// Příklad: text v závorce: (abc * def * (x + y)) je tedy vrácen shodně: (abc * def * (x + y))
		/// Text v oddělovačích «{proměnná}» je vrácen včetně oddělovačů: «{proměnná}»
		/// Na rozdíl od property InnerText, která vrací text bez vnějších oddělovačů.
        /// </summary>
        public string Text
        {
            get
            {
				if (this.HasInnerSegment) return this.InnerSegment.Text;
                if (this.HasContent) return this.Content;
                return "";
            }
        }
		/// <summary>
		/// Text segmentu, bez úvodního a koncového textu (uvozovky, závorky), ale obsahuje vnořené vnitřní hodnoty.
		/// Pozor: vnitřní vnořené hodnoty ale mají uvedeny i svoje úvodní a koncové texty.
		/// Příklad: text v závorce: (abc * def * (x + y)) je tedy vrácen bez vnějších závorek, ale s vnitřními závorkami: abc * def * (x + y)
		/// Text v oddělovačích «{proměnná}» je vrácen bez oddělovačů: proměnná
		/// Na rozdíl od property Text, která vrací text včetně vnějších oddělovačů.
		/// </summary>
		public string InnerText
		{
			get
			{
				if (this.HasInnerSegment) return this.InnerSegment.InnerText;
				if (this.HasContent) return this.Content;
				return "";
			}
		}
		/// <summary>
		/// Text segmentu do SimpleTextu.
		/// Pokud jde o Value, která obsahuje InnerSegment, pak se z InnerSegmentu čte 
		/// jeho InnerSimpleText (tři tečky ohraničené originálním oddělovačem).
		/// </summary>
		public string SimpleText
		{
			get
			{
				if (this.HasInnerSegment) return this.InnerSegment.SimplifiedText;
				if (this.HasContent) return this.Content;
				return "";
			}
		}
        #endregion
        #region Metody pro přidávání a změny
        /// <summary>
        /// Přidá do sebe další kousek textu do prvku Content.
        /// Texty je povoleno přidávat (tj. slučovat) jen pro prvky obsahující Text nebo Blank.
        /// Prvky obsahující Delimiter se již zakládají se zadaným textovým obsahem, a nelze je doplňovat.
        /// Prvky obsahující Segment vůbec nemají text.
        /// Pro prvky ostatních typů nelze tuto metodu použít, vyvolá chybu.
        /// </summary>
        /// <param name="text"></param>
        public void AddContent(string text)
        {
            this.AddContent(text, false);
        }
        /// <summary>
        /// Přidá do sebe další kousek textu do prvku Content.
        /// Bez problémů pracuje pro Blank a Text.
        /// Pro ostatní typy: pokud je povoleno konvertovat na text (parametr convertToText = true), pak sebe převede na Text a pak na konec přidá svůj text.
        /// Jinak vyhodí chybu.
        /// </summary>
        /// <param name="text"></param>
        public void AddContent(string text, bool convertToText)
        {
            switch (this.ValueType)
            {
                case ParserSegmentValueType.Blank:
                case ParserSegmentValueType.Text:
                    if (text != null)
                    {
                        this.ContentBuilder.Append(text);
                        this.ContentCache = null;
                    }
                    break;
                default:
                    if (!convertToText)
                        throw new InvalidOperationException("Nelze přidat text do položky ParserSegmentValue, která obsahuje typ dat " + this.ValueType.ToString());
                    else
                    {
                        string content = this.Text;
                        this.ContentCache = null;
                        this.ContentBuilder = new StringBuilder(content);
                        this.ValueType = ParserSegmentValueType.Text;
                        this._InnerSegment = null;

                        if (text != null)
                            this.ContentBuilder.Append(text);
                    }
                    break;
            }
        }
        /// <summary>
        /// Do objektu this přidá na svůj konec další objekt.
        /// Pokud přidávaná value je Blank, nic nepřidávám.
        /// Pokud this je Text, a současně přidávaná value je Text, pak provede sloučení textů a nemění charakter this.
        /// Pokud this je Blank, a současně přidávaná value je Text, pak sebe změní na Text a vloží do sebe text z přidávané value.
        /// Pokud this nebo přidávaná value není text, převede sebe na InnerSegment a svůj obsah vloží do první Value tohoto segmentu.
        /// </summary>
        /// <param name="value"></param>
        public void AppendValue(ParserSegmentValue value)
        {
            if (value.ValueType == ParserSegmentValueType.Blank) return;          // Blank nepřidávám.
            if (this.ValueType == ParserSegmentValueType.Text && value.ValueType == ParserSegmentValueType.Text)
                this.AddContent(value.Text);
            else if (this.ValueType == ParserSegmentValueType.Blank && value.ValueType == ParserSegmentValueType.Text)
            {
                this.ContentBuilder = new StringBuilder(value.Text);
                this.ContentCache = null;
            }
            else
            {
                if (!this.HasInnerSegment)
                    this.ConvertToContainer();
                this.InnerSegment.AddValue(value);
            }
        }
        /// <summary>
        /// Převede sebe sama na Container = vytvoří v sobě _InnerSegment, vloží do něj svůj Text jako text, a zapomene na svůj Content.
        /// Metoda ještě není definitivní, vytvářený segment vytváří jako klon segmentu this.Parent (=přebírá z něj vlastnosti), což nevím zda je OK ...)
        /// </summary>
        protected void ConvertToContainer()
        {
            string text = this.Text;

            this.ValueType = ParserSegmentValueType.InnerSegment;

            this._InnerSegment = new ParserSegment(this, this.Parent);
            this._InnerSegment.AddText(text);

            this.ContentBuilder = new StringBuilder();
            this.ContentCache = null;
        }
        #endregion
    }
	/// <summary>
	/// Druh obsahu v této položce
	/// </summary>
	public enum ParserSegmentValueType
	{
        /// <summary>
        /// Nic
        /// </summary>
		None = 0,
        /// <summary>
        /// Blank: mezery, tab, eol
        /// </summary>
		Blank,
        /// <summary>
        /// Přímý text mimo oddělovače
        /// </summary>
		Text,
        /// <summary>
        /// Oddělovače deklarované v segmentu
        /// </summary>
		Delimiter,
        /// <summary>
        /// Vnořený segment
        /// </summary>
		InnerSegment
	}
    #endregion
	#region class ParserSetting : informace pro textové parsování.
	/// <summary>
	/// ParserSetting : informace pro textové parsování.
	/// Obsahuje sadu definic segmentů.
	/// Segment je například string, závorka, komentář, text.
	/// Definice segmentů viz třída ParserSegmentSetting.
	/// </summary>
	public class ParserSetting
	{
		#region Konstrukce a základní property
		public ParserSetting(string name, string initialSegmentName)
		{
			this.Name = name;
			this.InitialSegmentName = initialSegmentName;
			this._Segments = new List<ParserSegmentSetting>();
		}
		/// <summary>
		/// Jméno settingu, informativní text
		/// </summary>
		public string Name { get; private set; }
		/// <summary>
		/// Název výchozího segmentu, kterým text začíná.
		/// Jeho pravidla platí tehdy, když začínáme, a pak vždy když skončí jeho vnořené segmenty.
		/// Tento segment nemusí mít delimitery (ale může).
		/// Tento segment by měl definovat, které vnořené bloky může obsahovat.
		/// </summary>
		public string InitialSegmentName { get; private set; }
		/// <summary>
		/// Definované vnitřní segmenty textu (čím začínají, jak se jmenují, čím končí, co mohou obsahovat, atd)
		/// </summary>
        public IEnumerable<ParserSegmentSetting> Segments { get { return this._Segments; } }
        private List<ParserSegmentSetting> _Segments;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			string text = this.Name;
			for (int i = 0; i < this._Segments.Count; i++)
				text += (i == 0 ? "" : ",") + this._Segments[i].SegmentName;

			return text;
		}
		#endregion
        /// <summary>
        /// Do nastavení segmentů přidá další položku
        /// </summary>
        /// <param name="segmentSetting"></param>
        public void SegmentSettingAdd(ParserSegmentSetting segmentSetting)
        {
            segmentSetting.Parent = this;
            this._Segments.Add(segmentSetting);
        }
	}
	#endregion
	#region class ParserSegmentSetting : Popisek jednoho segmentu (část parsovaného kódu).
	/// <summary>
	/// ParserSegmentSetting : Popisek jednoho segmentu (část parsovaného kódu).
	/// Zde se definuje: jak se segment jmenuje, čím začíná, čím končí, co vnořeného může obsahovat...
	/// </summary>
    public class ParserSegmentSetting
	{
		#region Konstrukce a základní property
        public ParserSegmentSetting(string segmentName)
		{
            this._Parent = null;
			this.SegmentName = segmentName;
            this.RtfCodePairs = new Dictionary<ParserSegmentRtfCodeKey, ParserSegmentRtfCodePair>();
		}
        /// <summary>
        /// Nastavení pro parsování, hlavička. Do uvedeného settingu patří nastavení segmentu (objekt this).
        /// </summary>
        public ParserSetting Parent
        {
            get { return this._Parent; }
            set
            {
                if (this._Parent != null)
                    throw new InvalidOperationException("Nelze změnit hodnotu ParserSegmentSetting.Parent poté, kdy byla jednou přiřazena.");
				this._Parent = value;
            }
        }
        private ParserSetting _Parent;
		/// <summary>
		/// Název segmentu, klíčové slovo
		/// </summary>
		public string SegmentName { get; private set; }
		/// <summary>
		/// Oddělovač, kterým tento segment začíná.
		/// </summary>
		public string BeginWith { get; set; }
		/// <summary>
		/// Texty, které jsou chápány jako blank (oddělovače bloků bez vlastního významu).
		/// Typicky jde o mezery, tabulátory, Cr, Lf.
		/// Text takového oddělovače je po parsování uložen do samostatného bloku, typu Blank.
		/// </summary>
		public string[] Blanks { get; set; }
		/// <summary>
		/// Texty, které jsou chápány jako významové oddělovače (mající vlastní význam).
		/// Typicky může jít o čárku, středník, operátory (+ - * /), znaky rovnosti a nerovnosti atd.
		/// Může jít i o řetězec delší než 1 znak (typicky "==", "!=", atd).
		/// Pak je při parsování detekován nejdelší odpovídající blok: 
		/// pokud je deklarován delimiter "=" i "==", pak při výskytu dvou rovnítek v textu je správně detekován a uložen delimiter "==".
		/// Text takového oddělovače je po parsování uložen do samostatného bloku, typu Delimiter.
		/// Každý detekovaný oddělovač je uložen do samostatného bloku. 
		/// Pokud po sobě následuje více oddělovačů, každý má svůj blok.
		/// </summary>
		public string[] Delimiters { get; set; }
		/// <summary>
		/// Oddělovače, kterými tento segment může skončit.
		/// </summary>
		public string[] EndWith { get; set; }
        /// <summary>
        /// true: Oddělovače na konci tohoto segmentu (this.EndWith) se nevkládají do konce segmentu (Segment.End), 
        /// ale zpracují se jako znaky následující za segmentem.
        /// Typicky jde o konec řádkového komentáře (Cr, Lf), které nejsou součástí komentáře, ale jde o standardní znak konce řádku v parent segmentu.
        /// </summary>
        public bool EndOutside { get; set; }
		/// <summary>
		/// Speciální bloky textu a jejich konverze
		/// </summary>
		public ParserSegmentSpecialTexts[] SpecialTexts { get; set; }
		/// <summary>
		/// Seznam segmentů, které se mohou vyskytovat uvnitř tohoto elementu.
		/// Typicky v závorce může být string nebo další závorky.
		/// </summary>
		public string[] InnerSegmentsNames { get; set; }
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			string text = this.SegmentName;
			if (!String.IsNullOrEmpty(this.BeginWith))
				text += " " + this.BeginWith;
			if (this.InnerSegmentsNames != null && this.InnerSegmentsNames.Length > 0)
			{
				for (int i = 0; i < this.InnerSegmentsNames.Length; i++)
					text += (i == 0 ? " {" : ", ") + this.InnerSegmentsNames[i];
				text += "} ";
			}
			if (this.EndWith != null && this.EndWith.Length > 0)
			{
				text += this.EndWith[0];
			}
			return text;
		}
		#endregion
		#region RtfCodes : deklarace RTF kódů pro různé druhy Values, jejich vkládání, hledání a podpora modifikace
        /// <summary>
        /// Přidá do segmentu deklaraci RTF kódů použitých na začátku určité hodnoty, dané typem.
        /// </summary>
        /// <param name="valueType">Typ hodnoty.
        /// Správné je předat typ: Blank, Delimiter, Text.
        /// Podle typu Text (bez názvu) se budou zobrazovat naše okraje (Begin a End, typicky závorky).
        /// Název je pro tuto položku nastaven na null.
        /// </param>
        /// <param name="codeSet">RTF kódy na začátku</param>
        /// <param name="codeReset">RTF kódy na konci</param>
        public void AddRtfCodes(ParserSegmentValueType valueType, RtfItem[] codeSet, RtfItem[] codeReset)
        {
            this.AddRtfCodes(new ParserSegmentRtfCodePair(valueType, codeSet, codeReset));
        }
        /// <summary>
        /// Přidá do segmentu deklaraci RTF kódů použitých na začátku určité hodnoty, dané názvem.
        /// </summary>
        /// <param name="valueName">Název páru.
        /// Typ hodnoty (ValueType) je pro tuto položku nastaven na ParserSegmentValueType.Text.</param>
        /// <param name="codeSet">RTF kódy na začátku</param>
        /// <param name="codeReset">RTF kódy na konci</param>
        public void AddRtfCodes(string valueName, RtfItem[] codeSet, RtfItem[] codeReset)
        {
            this.AddRtfCodes(new ParserSegmentRtfCodePair(valueName, codeSet, codeReset));
        }
        /// <summary>
        /// Přidá deklaraci RTF kódů.
        /// Kontroluje duplicitu.
        /// </summary>
        /// <param name="rtfCodePair"></param>
        protected void AddRtfCodes(ParserSegmentRtfCodePair rtfCodePair)
        {
            if (this.RtfCodePairs.ContainsKey(rtfCodePair))
                throw new ArgumentException("Definice RTF kódů pro klíč " + rtfCodePair.ToString() + " již v definici segmentu " + this.SegmentName + " existuje. Nelze přidat duplicitní položku.");
            this.RtfCodePairs.Add(rtfCodePair, rtfCodePair);
        }
        /// <summary>
        /// Obsahuje (vrací) sadu všech RTF kódů, vrací celé páry.
        /// Používá se typicky při modifikaci obsažených RTF kódů (CodeSet, CodeReset).
        /// Touto cestou nelze přidat/odebrat celé definice ani změnit jejich klíče (Type, Name).
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ParserSegmentRtfCodePair> AllRtfCodePairs
        {
            get { return this.RtfCodePairs.Values; }
        }
        /// <summary>
        /// Do RTF kodéru vloží RTF kódy Set pro daný segment.
        /// </summary>
        /// <param name="rtfCoder"></param>
        /// <param name="value"></param>
        internal void RtfCoderAddCodesSet(List<RtfItem> rtfItems, ParserSegment segment)
        {
            ParserSegmentRtfCodePair codePair = this.FindRtfCodePair(new ParserSegmentRtfCodeKey(ParserSegmentValueType.Text, (string)null));
            if (codePair == null || codePair.CodeSet == null || codePair.CodeSet.Length == 0) return;
            rtfItems.AddRange(codePair.CodeSet);
        }
        /// <summary>
        /// Do RTF kodéru vloží RTF kódy Set pro danou položku (value).
        /// </summary>
        /// <param name="rtfCoder"></param>
        /// <param name="value"></param>
        internal void RtfCoderAddCodesSet(List<RtfItem> rtfItems, ParserSegmentValue value)
        {
            ParserSegmentRtfCodePair codePair = this.FindRtfCodePair(value);
            if (codePair == null || codePair.CodeSet == null || codePair.CodeSet.Length == 0) return;
            rtfItems.AddRange(codePair.CodeSet);
        }
        /// <summary>
        /// Do RTF kodéru vloží RTF kódy Reset pro daný segment.
        /// </summary>
        /// <param name="rtfCoder"></param>
        /// <param name="value"></param>
        internal void RtfCoderAddCodesReset(List<RtfItem> rtfItems, ParserSegment segment)
        {
            ParserSegmentRtfCodePair codePair = this.FindRtfCodePair(new ParserSegmentRtfCodeKey(ParserSegmentValueType.Text, null));
            if (codePair == null || codePair.CodeReset == null || codePair.CodeReset.Length == 0) return;
            rtfItems.AddRange(codePair.CodeReset);
        }
        /// <summary>
        /// Do RTF kodéru vloží RTF kódy Reset pro danou položku (value).
        /// </summary>
        /// <param name="rtfCoder"></param>
        /// <param name="value"></param>
        internal void RtfCoderAddCodesReset(List<RtfItem> rtfItems, ParserSegmentValue value)
        {
            ParserSegmentRtfCodePair codePair = this.FindRtfCodePair(value);
            if (codePair == null || codePair.CodeReset == null || codePair.CodeReset.Length == 0) return;
            rtfItems.AddRange(codePair.CodeReset);
        }
        /// <summary>
        /// Vrátí definici RTF kódů (pár Set + Reset) pro danou položku (value).
        /// Pokud neexistuje, vrací null.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public ParserSegmentRtfCodePair FindRtfCodePair(ParserSegmentValue value)
        {
            if (value == null) return null;
            ParserSegmentRtfCodeKey key = new ParserSegmentRtfCodeKey(value.ValueType, (value.ValueType == ParserSegmentValueType.Text ? value.ValueName : (string)null));
            return FindRtfCodePair(key);
        }
        /// <summary>
        /// Vrátí definici RTF kódů (pár Set + Reset) pro daný klíč (key).
        /// Pokud neexistuje, vrací null.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public ParserSegmentRtfCodePair FindRtfCodePair(ParserSegmentRtfCodeKey key)
        {
            if (key == null) return null;
            ParserSegmentRtfCodePair pair;
            // Hledaný prvek:
            if (this.RtfCodePairs.TryGetValue(key, out pair))
                return pair;
            // Náhradní prvek:
            if (this.RtfCodePairs.TryGetValue(new ParserSegmentRtfCodeKey(ParserSegmentValueType.Text, null), out pair))
                return pair;
            return null;
        }
        /// <summary>
        /// Dictionary obsahující RtfCodePair. Obsahuje je jak v Key (hledá se podle nich) tak ve Values = reálně uložené hodnoty.
        /// </summary>
        protected Dictionary<ParserSegmentRtfCodeKey, ParserSegmentRtfCodePair> RtfCodePairs { get; private set; }
		#endregion
	}
	#endregion
    #region class ParserSegmentRtfCodeKey a ParserSegmentRtfCodePair : podpora pro RTF kódování obsahu (Values) segmentu.
    /// <summary>
    /// ParserSegmentRtfCodeKey : klíč pro pár RTF kódů.
    /// Pracuje s typem hodnoty a s jejím názvem (ValueType a ValueName).
    /// Slouží jako klíč, nenese RTF data.
    /// Funguje jako předek třídy ParserSegmentRtfCodePair.
    /// </summary>
    public class ParserSegmentRtfCodeKey
    {
        #region Konstrukce, overrides
        /// <summary>
        /// Vytvoří ParserSegmentRtfCodeKey pro daný klíč a název, pro vyhledávání v Dictionary.
        /// </summary>
        /// <param name="valueType">Typ hodnoty.</param>
        /// <param name="valueName">Název hodnoty</param>
        internal ParserSegmentRtfCodeKey(ParserSegmentValue value)
        {
            this.ValueType = value.ValueType;
            this.ValueName = value.ValueName;
            this._SetHashcode();
        }
        /// <summary>
        /// Vytvoří ParserSegmentRtfCodeKey pro daný klíč a název, pro vyhledávání v Dictionary.
        /// </summary>
        /// <param name="valueType">Typ hodnoty.</param>
        /// <param name="valueName">Název hodnoty</param>
        internal ParserSegmentRtfCodeKey(ParserSegmentValueType valueType, string valueName)
        {
            this.ValueType = valueType;
            this.ValueName = valueName;
            this._SetHashcode();
        }
        public override bool Equals(object obj)
        {
            ParserSegmentRtfCodeKey other = obj as ParserSegmentRtfCodeKey;
            if ((object)other == (object)null) return false;
            if (this.ValueType != other.ValueType) return false;
            return String.Equals(this.ValueName, other.ValueName);
        }
        public override int GetHashCode()
        {
            return this._Hashcode;
        }
        private void _SetHashcode()
        {
            if (!String.IsNullOrEmpty(this.ValueName))
                _Hashcode = this.ValueType.GetHashCode() ^ this.ValueName.GetHashCode();
            else
                _Hashcode = this.ValueType.GetHashCode() ^ 0x7654321;
        }
        private int _Hashcode;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string text = this.ValueType.ToString();
            if (!String.IsNullOrEmpty(this.ValueName))
                text += ": " + this.ValueType;
            return text;
        }
        public static bool operator ==(ParserSegmentRtfCodeKey a, ParserSegmentRtfCodeKey b)
        {
            bool aIsNull = (object)a == (object)null;
            bool bIsNull = (object)b == (object)null;
            if (aIsNull && bIsNull) return true;           // Pokud jsou oba objekty null, pak == true
            if (aIsNull || bIsNull) return false;          // Pokud je jeden objekt null, a druhý ne, pak je == false
            return a.Equals(b);
        }
        public static bool operator !=(ParserSegmentRtfCodeKey a, ParserSegmentRtfCodeKey b)
        {
            bool aIsNull = (object)a == (object)null;
            bool bIsNull = (object)b == (object)null;
            if (aIsNull && bIsNull) return false;          // Pokud jsou oba objekty null, pak != false
            if (aIsNull || bIsNull) return true;           // Pokud je jeden objekt null, a druhý ne, pak je != true
            return !a.Equals(b);
        }
        #endregion
        #region Property
        /// <summary>
        /// Typ, pro který se tento RTF kód využije
        /// </summary>
        public ParserSegmentValueType ValueType { get; private set; }
        /// <summary>
        /// Název hodnoty, pro který se tento RTF kód využije
        /// </summary>
        public string ValueName { get; private set; }
        #endregion
    }
    /// <summary>
    /// ParserSegmentRtfCodePair : podpora pro RTF kódování obsahu (Values) segmentu.
    /// Obsahuje typ (ValueType), název (PairName) a pak dvě pole RTF kódů: jedno definuje RTF kódy vkládané před určitou část (část je daná jménem) a druhé definuje RTF kódy vložené za tuto část.
    /// Objekt této třídy (ParserSegmentRtfCodePair) může hrát roli klíče v Dictionary.
    /// </summary>
    public class ParserSegmentRtfCodePair : ParserSegmentRtfCodeKey
    {
        #region Konstrukce
        /// <summary>
        /// Vytvoří RtfCodePair pro daný typ hodnoty.
        /// Název je pro tuto položku nastaven na null.
        /// </summary>
        /// <param name="valueType">Typ hodnoty.
        /// Správné je předat typ: Blank, Delimiter, Text.
        /// Podle typu Text (bez názvu) se budou zobrazovat naše okraje (Begin a End, typicky závorky).
        /// Název je pro tuto položku nastaven na null.
        /// </param>
        /// <param name="codeSet">RTF kódy na začátku</param>
        /// <param name="codeReset">RTF kódy na konci</param>
        public ParserSegmentRtfCodePair(ParserSegmentValueType valueType, RtfItem[] codeSet, RtfItem[] codeReset)
            : base(valueType, null)
        {
            this.CodeSet = codeSet;
            this.CodeReset = codeReset;
        }
        /// <summary>
        /// Vytvoří RtfCodePair pro typ hodnoty Text pro daný název. 
        /// "Název" je uživatelská definice, umožňuje barevně odlišovat různé texty (Keyword, Numbers, Functions, atd).
        /// Z hlediska Parseru jsou to všechno Texty, ale z hlediska Lexeru mají různý význam.
        /// Typ hodnoty (ValueType) je pro tuto položku nastaven na ParserSegmentValueType.Text.
        /// </summary>
        /// <param name="valueName">Název páru.
        /// Typ hodnoty (ValueType) je pro tuto položku nastaven na ParserSegmentValueType.Text.</param>
        /// <param name="codeSet">RTF kódy na začátku</param>
        /// <param name="codeReset">RTF kódy na konci</param>
        public ParserSegmentRtfCodePair(string valueName, RtfItem[] codeSet, RtfItem[] codeReset)
            : base(ParserSegmentValueType.Text, valueName)
        {
            this.CodeSet = codeSet;
            this.CodeReset = codeReset;
        }
        #endregion
        #region Property
        /// <summary>
        /// RTF kódy na začátku textu
        /// </summary>
        public RtfItem[] CodeSet { get; set; }
        /// <summary>
        /// RTF kódy na konci textu
        /// </summary>
        public RtfItem[] CodeReset { get; set; }
        #endregion
    }
    #endregion
    #region class ParserSegmentSpecialTexts : Speciální bloky textu a jejich konverze
	/// <summary>
	/// ParserSegmentSpecialTexts : Speciální bloky textu a jejich konverze
	/// </summary>
    public class ParserSegmentSpecialTexts
	{
		public ParserSegmentSpecialTexts(string inputText, string outputText)
		{
			this.InputText = inputText;
			this.OutputText = outputText;
		}
		/// <summary>
		/// Text, který pokud bude nalezen, značí speciální text.
		/// V C# například \" značí jednu uvozovku, \r značí CR.
		/// V SQL například '' značí '
		/// </summary>
		public string InputText { get; private set; }
		/// <summary>
		/// Text, který se zapisuje do textu segmentu namísto vstupujícího textu.
		/// </summary>
		public string OutputText { get; private set; }
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.InputText + " => " + this.OutputText;
		}
	}
	#endregion

    #region Služby jazykové závislé
    /// <summary>
    /// Služby pro SQL parsovaná data
    /// </summary>
    public class ParserSqlLibrary
    {
        /// <summary>
        /// Sloučí values ze všech dodaných segmentů do jednoho Listu
        /// </summary>
        /// <param name="segments"></param>
        /// <returns></returns>
        public static List<ParserSegmentValue> JoinValues(IEnumerable<ParserSegment> segments)
        {
            List<ParserSegmentValue> result = new List<ParserSegmentValue>();
            foreach (ParserSegment segment in segments)
                result.AddRange(segment.Values);
            return result;
        }
        /// <summary>
        /// Vyhledá a vrátí řadu ParserSegmentValue (=slova SQL příkazu), která odpovídají danému zadání (pattern).
        /// Pokud se hledaný pattern nenajde, vrátí null.
        /// Pokud zadaný pattern není zadán (počet položek = 0), vrátí se null = nenalezeno.
        /// V patternu může být obsaženo null = na dané pozici může být libovolná Value (většinou ta, kterou hledáme).
        /// Zatím nelze definovat libovolný počet libovolných hodnot, ani nelze definovat Wildcards nebo jinou formu filtrování (predikát).
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static List<ParserSegmentValue> SearchText(IEnumerable<ParserSegmentValue> values, params string[] pattern)
        { 
            int patCnt = pattern.Length;
            if (patCnt == 0) return null;

            // Seznam významově podstatných hodnot (přímý text, proměnné, delimitery, závorky a stringy a sysnames jako ucelené segmenty). Bez komentářů, bez Blank:
            List<ParserSegmentValue> valueList = FilterSqlValueList(values);
            int valCnt = valueList.Count;

            int searchFrom = 0;
            while (true)
            {
                // Pokud (searchFrom) je tak daleko, že počet hledaných položek (patCnt) už vychází za počet Values (valCnt), pak jsme prostě nic nenašli:
                if ((searchFrom + patCnt) >= valCnt)
                    return null;

                // Položek je dostatek, můžeme testovat po sobě jdoucí hodnoty (values) proti pattern:
                bool match = true;
                for (int i = 0; i < patCnt; i++)
                {
                    if (!IsMatch(valueList[searchFrom + i], pattern[i]))
                    {
                        match = false;
                        break;
                    }
                }
                if (match)
                    return valueList.GetRange(searchFrom, patCnt);

                // Budeme hledat od následující položky:
                searchFrom++;
            }
        }
        /// <summary>
        /// Vrátí true, pokud text z dané hodnoty (value.Text) odpovídá textu zadanému jako pattern
        /// </summary>
        /// <param name="value"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private static bool IsMatch(ParserSegmentValue value, string pattern)
        {
            if (pattern == null) return true;
            return String.Equals(value.Text, pattern, StringComparison.InvariantCultureIgnoreCase);
        }
        /// <summary>
        /// Vrátí soupis hodnot významných z hlediska SQL.
        /// Vynechává hodnoty Blank, None a vynechává segmenty s komentářem.
        /// Následně slučuje hodnoty pomocí tečkové konvence, včetně slučování SysNames.
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static List<ParserSegmentValue> FilterSqlValueList(IEnumerable<ParserSegmentValue> values)
        {
            // Posbírat významové values:
            List<ParserSegmentValue> result = new List<ParserSegmentValue>();
            foreach (ParserSegmentValue value in values)
            {
                if (IsSqlText(value))
                    result.Add(value);
            }

            // Sloučit sousední values, pokud mezi nimi jsou tečky:
            for (int i = 0; i < (result.Count - 1); i++)
            {
                ParserSegmentValue value0 = result[i];
                ParserSegmentValue value1 = result[i + 1];
                if (IsDotJoin(value0, value1))
                {
                    value0.AddContent(value1.Text, true);
                    result.RemoveAt(i + 1);
                    i--;
                }
            }

            return result;
        }
        /// <summary>
        /// Vrátí true, pokud dva objekty Value lze sloučit tečkovou konvencí do jednoho
        /// </summary>
        /// <param name="value0"></param>
        /// <param name="value1"></param>
        /// <returns></returns>
        private static bool IsDotJoin(ParserSegmentValue value0, ParserSegmentValue value1)
        {
            return (value0.Text.EndsWith(".") || value1.Text.StartsWith("."));
        }
        /// <summary>
        /// Vrátí true, pokud daná hodnota je významá pro SQL text
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsSqlText(ParserSegmentValue value)
        {
            if (value.ValueType == ParserSegmentValueType.Delimiter) return true;         // Delimiter je SQL text
            if (value.ValueType == ParserSegmentValueType.Text) return true;              // Text je SQL text
            if (value.ValueType != ParserSegmentValueType.InnerSegment) return false;     // Ostatní kromě InnerSegment nejsou SQL text (jde o : None a Blank).

            if (value.InnerSegment.SegmentName == ParserDefaultSetting.SQL_COMMENTLINE) return false;   // Komentáře nebrat
            if (value.InnerSegment.SegmentName == ParserDefaultSetting.SQL_COMMENTBLOCK) return false;

            return true;     // Ostatní je SQL text (jde o : SqlCode, SqlLiteral, SqlSysName, SqlParenthesis).
        }
    }
    #endregion
}
